Skip to content

fix(protocol): pin AdCP 3.1 stable#953

Merged
bokelley merged 1 commit into
mainfrom
pin-adcp-3-1-release
Jun 18, 2026
Merged

fix(protocol): pin AdCP 3.1 stable#953
bokelley merged 1 commit into
mainfrom
pin-adcp-3-1-release

Conversation

@bokelley

@bokelley bokelley commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Pin the bundled AdCP protocol version to the final 3.1.0 release and regenerate schemas/types from the verified protocol bundle.
  • Remove beta/prerelease release configuration and update release docs/classifier for stable v6 behavior.
  • Add DeclaredBy collision handling and explicit aliases for provenance and SI sponsored context types.

Validation

  • PYTHONPATH=src python3 -m pytest tests/test_schemas_version_pin.py tests/test_validation_version.py tests/test_adcp_version_option.py tests/test_dispatcher_version_routing.py tests/test_schema_validation.py tests/test_sync_schemas.py tests/test_collision_guard.py tests/test_collision_aliases.py -q
  • make validate-generated
  • PYTHONPATH=src python3 -m pytest tests/test_public_api.py tests/test_collision_aliases.py tests/test_type_aliases.py -q
  • ruff check scripts/consolidate_exports.py src/adcp/types/__init__.py src/adcp/types/aliases.py tests/test_collision_aliases.py
  • ruff check scripts/post_generate_fixes.py scripts/consolidate_exports.py src/adcp/types/__init__.py src/adcp/types/aliases.py tests/test_collision_aliases.py
  • PYTHONPATH=src python3 -m pytest tests/test_schemas_version_pin.py tests/test_collision_aliases.py tests/test_public_api.py -q
  • PYTHONPATH=src python3 -m pytest tests/test_canonical_formats_registry.py tests/test_canonical_formats_roundtrip.py tests/test_canonical_formats_v1_to_v2.py tests/test_decisioning_specialisms.py::test_signal_owned_manifest_exercises_discovery_only -q
  • PYTHONPATH=src python3 -m pytest tests/test_schemas_version_pin.py tests/test_validation_version.py tests/test_dispatcher_version_routing.py tests/test_public_api.py tests/test_collision_aliases.py tests/test_normalize_pyproject_prerelease.py -q
  • ruff check src/adcp/canonical_formats/registry.py tests/test_decisioning_specialisms.py
  • Pre-commit on final squashed commit: black, ruff, mypy, adopter type checks, bandit, JSON, whitespace/EOF, merge/case conflict, large file, and private-key hooks.
  • Expert protocol/code review passes; both converged on the release-metadata handoff, which is now documented below.

Local package build was not run because python3 -m build is not installed in this workspace.

Release Notes

pyproject.toml, .release-please-manifest.json, and CHANGELOG.md intentionally still carry the current beta package version in this spec-pin PR. Release Please owns those release-file updates.

After this lands, the existing Release Please PR (#949, autorelease: pending) should update to the stable 6.3.0 release because this PR removes prerelease mode from release-please-config.json. Merging that Release Please PR is the step that should create the GitHub release/tag and publish to PyPI.

@bokelley bokelley force-pushed the pin-adcp-3-1-release branch from e519e9b to 4ac58bb Compare June 18, 2026 11:55
@bokelley bokelley marked this pull request as ready for review June 18, 2026 11:55

@aao-ipr-bot aao-ipr-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The spec-pin itself is sound — clean rc.13→3.1.0 regeneration with the right additive surface. Holding off on approve for one reason: the release handoff this PR describes contradicts the live state of the PR it hands off to, and the workflow auto-publishes on merge.

Things I checked

  • Public surface is additive-only. 0 files removed across all 855; public_api_snapshot.json gains exactly ProvenanceDeclaredBy and SiSponsoredContextDeclaredBy, no removals. fix(protocol): is the correct semver signal — no breaking change, no ! needed.
  • DeclaredBy collision wiring is internally consistent across all five touched files (consolidate_exports.py:96 collision set → aliases.py qualified imports + __all____init__.py re-export block at L640/657, inside the from adcp.types.aliases import window, not the direct-generated_poc block → snapshot → test_collision_aliases.py:60-61,165). code-reviewer: clean.
  • The two DeclaredBy types are genuinely distinct, so two qualified aliases is the right shape, not a union. core.provenance Role {creator, advertiser, agency, platform, tool} vs si_sponsored_context Role {brand_agent, seller, network, platform} — only platform overlaps. Merging would illegally widen each object's role enum on the wire. ad-tech-protocol-expert: sound. Same mechanism as the existing Signal/Unit qualified aliases.
  • registry_event.py status: Literal['removed'] narrowing is faithful to the 3.1 registry-event.json collection.removed arm (const: "removed", parent Status | None). The # type: ignore[assignment] added by post_generate_fixes.py is error-code-scoped, not blanket — within the CI floor.
  • Forward-compat intact. _forward_compat.py / _ergonomic.py untouched; UnknownFormatAsset/UnknownGroupAsset fallback arms still present (aliases.py L1797-1870). The RegistryEventN/PayloadN renumber churn is expected datamodel-code-generator traversal-order noise, not a semantic delta.
  • Generated diff is regeneration output, not hand-editedregistry_event.py matches the new post_generate_fixes function exactly.

The open question (what flips this to approve)

dx-expert verified against the live repo:

  • This PR body asserts pending Release Please PR #949 "should update to the stable 6.3.0 release."
  • #949 currently reads 6.0.0-beta.1 (manifest + pyproject 6.0.0b1). main is at 6.3.0-beta.10; latest published tag is v6.3.0-beta.9.
  • release-please.yml runs release_created → build → twine upload in the same job — merge of the RP PR auto-publishes with no gate.

So if #949 is merged in its current shape after prerelease mode is dropped, it most likely publishes a stable 6.0.0 — below the already-published 6.3.0-beta.9, non-monotonic and unyankable. Manifest history shows this class of footgun already fired once (55b5bfcf chore: keep release on 6.3 beta line, rescuing a 7.0.0-beta.7 miscompute).

This is not a defect in this diff — this PR doesn't bump the manifest, tag, or publish. But this PR is the trigger that changes RP's recompute, and the documented handoff is currently false against #949's state. What flips me to approve:

  1. Confirm that after this lands, RP re-runs and updates #949's title/manifest to 6.3.0 (monotonic above v6.3.0-beta.9) — and that #949 is not merged while it still reads 6.0.0-beta.1.
  2. (Optional, but it's what the prior rescue did) Anchor the base explicitly rather than trusting the suffix-strip to graduate cleanly.

Follow-ups (non-blocking — file as issues)

  • A CI guard that refuses to tag/publish a version <= the latest published tag would convert this whole class of release regression from a silent PyPI publish into a failed workflow. Two near-misses now.
  • fix(protocol): yields a patch bump — confirm post-merge the tag lands where intended, since this is the prerelease→stable cutover.

Diff is sound; the hold is on the release-coordination question, not the code. Answer (1) and I'll approve.

@bokelley bokelley force-pushed the pin-adcp-3-1-release branch from 4ac58bb to b97ce8d Compare June 18, 2026 12:06

@aao-ipr-bot aao-ipr-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean version pin. The bundle-key indirection is the right shape: collapsing 3.1.03.1 is exactly what makes the on-disk lookup find the regenerated bundle once the rc suffix drops.

Things I checked

  • resolve_bundle_key wiring is load-bearing and correct. resolve_bundle_key("3.1.0")"3.1" via the no-prerelease branch (validation/version.py:69-73), which matches the shipped schemas/cache/3.1/ layout. This isn't cosmetic — pre-pin the rc kept its full id (3.1.0-rc.13) as the cache key so the old raw-adcp_version lookup worked; pinning to stable collapses the key to 3.1, so the resolver is the fix, not decoration. Both lookup paths in canonical_formats/registry.py:54,74 use bundle_key, and the FileNotFoundError message carries it.
  • No import cycle. adcp.validation.version imports only re — the new from adcp.validation.version import resolve_bundle_key in registry.py can't cycle back through adcp.types / adcp.canonical_formats.
  • Public surface is additive-only. public_api_snapshot.json diff is two adds (ProvenanceDeclaredBy, SiSponsoredContextDeclaredBy), zero removals — no required↔optional flip, no enum-member drop, no response-model reshape. fix(protocol): with no ! is the correct semver signal for the negotiated wire contract.
  • DeclaredBy collision is real, not a codegen artifact. ad-tech-protocol-expert: sound. core/provenance role enum [creator, advertiser, agency, platform, tool] vs si_sponsored_context role enum [brand_agent, seller, network, platform] — only platform overlaps; unifying them would corrupt both. Disambiguation reuses the existing test-backed collision machinery (consolidate_exports.py:96, aliases wired across __init__.py / aliases.py / snapshot / test_collision_aliases.py). Right shape.
  • The # type: ignore[assignment] is a narrowing marker, not a masked bug. Payload4(CollectionPayload) narrows inherited status: Status | None to Literal['removed']; 'removed' is a real Status member. ad-tech-protocol-expert confirmed this renders registry-event.json's allOf + const: "removed" faithfully. Specific code, not blanket. The fixer in post_generate_fixes.py:3439 is idempotent and applied at regeneration time — generated code stays generated.
  • Apparent field removals in registry_event / tasks_get_response are renumber-window churn. ad-tech-protocol-expert verified Payload6/Payload7 still carry their original event payloads — the anonymous-variant numbers just shifted after the additive collection.* branches landed. Discarded as churn per CLAUDE.md.

Follow-ups (non-blocking — file as issues)

  • Semver prose vs prefix. ad-tech-protocol-expert flagged that the regeneration pulls in net-new optional wire surface (SI sponsored-context family, registry_event collection.* event branches, DistributionIdentifierType:+youtube_channel_*, MediaBuy:+status_as_of). All additive, none breaking — but the most precise conventional-commit type for net-new fields/enum members is feat:, not fix:. No wire defect either way, and since Release Please targets a minor bump (6.3.0) the version math lands correctly regardless. Worth a one-line policy confirmation, nothing more.
  • Package build unrun. PR body notes python3 -m build isn't installed in the workspace, so the local package build was skipped. Non-critical — Release Please owns publish — but CI should confirm the wheel builds against the Production/Stable classifier before #949 merges.

The version envelope negotiates at release precision (3.1), so rc.13 and stable advertise the same contract — bokelley.

@bokelley bokelley merged commit 9cc9fa6 into main Jun 18, 2026
27 checks passed
@bokelley bokelley deleted the pin-adcp-3-1-release branch June 18, 2026 12:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant